package com.thornbird.vehiclerecycling.service.impl;

import com.thornbird.vehiclerecycling.entity.Config;
import com.thornbird.vehiclerecycling.entity.Order;
import com.thornbird.vehiclerecycling.entity.User;
import com.thornbird.vehiclerecycling.exception.ForbiddenException;
import com.thornbird.vehiclerecycling.exception.NotFoundException;
import com.thornbird.vehiclerecycling.exception.ValidationException;
import com.thornbird.vehiclerecycling.model.record.OrderRecord;
import com.thornbird.vehiclerecycling.model.record.PageList;
import com.thornbird.vehiclerecycling.model.record.UserRecord;
import com.thornbird.vehiclerecycling.repository.ConfigRepository;
import com.thornbird.vehiclerecycling.repository.OrderRepository;
import com.thornbird.vehiclerecycling.repository.UserRepository;
import com.thornbird.vehiclerecycling.service.OrderService;
import com.thornbird.vehiclerecycling.util.DataUtil;
import com.thornbird.vehiclerecycling.util.DateUtil;
import com.thornbird.vehiclerecycling.util.EncryptUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.MessageFormat;
import java.util.*;

@Service
@Configuration
@Transactional(rollbackFor = Exception.class)
public class OrderServiceImpl implements OrderService {

    @Value("${app.host-name}")
    private String hostName;

    @Value("${app.check-host-name}")
    private String checkHostName;

    @Value("${app.order.check-url}")
    private String checkUrl;

    private final OrderRepository orderRepository;

    private final UserRepository userRepository;

    private final ConfigRepository configRepository;

    @Autowired
    public OrderServiceImpl(OrderRepository orderRepository, UserRepository userRepository,
                            ConfigRepository configRepository) {
        this.orderRepository = orderRepository;
        this.userRepository = userRepository;
        this.configRepository = configRepository;
    }

    @Override
    public PageList<OrderRecord> findOrders(String keyword, Date startDate, Date endDate,
                                            Integer pageNumber, Integer pageSize) {
        Pageable pageable = PageList.generatePageable(pageNumber, pageSize);
        keyword = keyword == null ? "" : keyword.toLowerCase();
        keyword = "%" + keyword + "%";
        if (startDate == null) {
            startDate = DateUtil.getData(1970, 0, 1);
        }
        if (endDate == null) {
            endDate = DateUtil.getData(9999, 12, 31);
        }
        Page<Order> page = orderRepository.findByCondition(keyword, startDate, endDate, pageable);
        PageList<OrderRecord> pageList = PageList.convertPage(page, OrderRecord.class, null, userRepository);
        pageList.setExtraData(getDataItems());
        for (OrderRecord orderRecord : pageList.getList()) {
            updateCheckUrl(orderRecord);
        }
        return pageList;
    }

    @Override
    public OrderRecord findOrder(Long id) throws NotFoundException {
        Order order = orderRepository.findFirstById(id);
        if (order == null) {
            throw new NotFoundException();
        }

        OrderRecord orderRecord = new OrderRecord();
        orderRecord.parseEntity(order);
        if (orderRecord.getUserId() != null) {
            User user = userRepository.findFirstById(orderRecord.getUserId());
            if (user != null) {
                UserRecord userRecord = new UserRecord();
                userRecord.parseEntity(user);
                orderRecord.setUser(userRecord);
            }
        }
        updateCheckUrl(orderRecord);
        return orderRecord;
    }

    @Override
    public OrderRecord saveOrder(Order order, Boolean generateCheckCode, UserRecord userRecord)
            throws NotFoundException, ValidationException, ForbiddenException {
        generateCheckCode = generateCheckCode == null ? false : generateCheckCode;

        Long id = null;
        Order currentOrder = null;
        if (order.getId() != null && order.getId() > 0) {
            currentOrder = orderRepository.findFirstById(order.getId());
            if (currentOrder != null) {
                id = currentOrder.getId();
            } else {
                throw new NotFoundException();
            }
        }
        order.setId(id);
        order = checkOrder(order, generateCheckCode, userRecord);

        boolean superUser = userRecord.getSuperUser() != null && userRecord.getSuperUser();
        if (!superUser && currentOrder != null && !currentOrder.getUserId().equals(userRecord.getId())) {
            throw new ForbiddenException();
        }

        if (!superUser && currentOrder != null && currentOrder.getCheckCode() != null &&
                !currentOrder.getCheckCode().isEmpty()) {
            throw new ForbiddenException();
        }

        if (order.getDeleted() != null && order.getDeleted()) {
            boolean active = currentOrder != null && currentOrder.getCheckCode() != null &&
                    !currentOrder.getCheckCode().isEmpty();
            if (!active) {
                throw new ValidationException("仅可对已生效订单进行作废");
            }
        }

        if (currentOrder == null) {
            order.setUserId(userRecord.getId());
        } else {
            order.setCreatedAt(currentOrder.getCreatedAt());
            order.setCheckCode(currentOrder.getCheckCode());
            order.setUserId(currentOrder.getUserId());
        }
        if (order.getDeleted() != null && order.getDeleted()) {
            order.setCheckCode(null);
        }
        orderRepository.save(order);

        if (generateCheckCode) {
            String checkCode = EncryptUtil.md5(EncryptUtil.getOrderedData(order, "updatedAt"));
            if (order.getCheckCode() == null || !order.getCheckCode().equals(checkCode)) {
                order.setCheckCode(checkCode);
                orderRepository.save(order);
            }
        } else {
            order.setCheckCode(null);
            orderRepository.save(order);
        }
        return findOrder(order.getId());
    }

    @Override
    public OrderRecord deleteOrder(Long id, UserRecord userRecord)
            throws NotFoundException, ValidationException, ForbiddenException {
        OrderRecord orderRecord = findOrder(id);
        if (orderRecord.getCheckCode() != null && !orderRecord.getCheckCode().isEmpty()) {
            throw new ValidationException("不能删除已生效订单");
        }

        if (orderRecord.getDeleted() != null && orderRecord.getDeleted()) {
            throw new ValidationException("不能删除已作废订单");
        }

        boolean superUser = userRecord.getSuperUser() != null && userRecord.getSuperUser();
        if (!superUser) {
            Long userId = orderRecord.getUserId();
            userId = userId == null ? 0 : userId;
            if (!userRecord.getId().equals(userId)) {
                throw new ForbiddenException();
            }
        }

        orderRepository.deleteById(id);
        return orderRecord;
    }

    @Override
    public String getCheckUrl() {
        if (checkHostName != null && !checkHostName.isEmpty()) {
            return checkHostName + checkUrl;
        }
        return hostName + checkUrl;
    }

    @Override
    public void updateCheckUrl(OrderRecord orderRecord) {
        if (orderRecord.getCheckCode() != null) {
            String url = getCheckUrl();
            int checkNumber = EncryptUtil.md52int(orderRecord.getCheckCode());
            url = MessageFormat.format(url, String.valueOf(orderRecord.getId()), String.valueOf(checkNumber));
            orderRecord.setCheckUrl(url);
        }
    }

    private Order checkOrder(Order o, boolean generateCheckCode, UserRecord userRecord)
            throws ValidationException, ForbiddenException {
        Order order = new Order();
        BeanUtils.copyProperties(o, order, "createdAt", "updatedAt", "checkCode", "userId");
        try {
            DataUtil.fillDefaultValue(order);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        boolean superUser = userRecord.getSuperUser() != null && userRecord.getSuperUser();
        String companyName = "";
        if (!superUser) {
            companyName = userRecord.getCompanyName();
            companyName = companyName == null ? "" : companyName;
            if (companyName.isEmpty()) {
                throw new ForbiddenException();
            }
        }

        if (order.getSerialNumber() == null || order.getSerialNumber().isEmpty()) {
            throw new ValidationException("流水号不能为空");
        }

        if (order.getCertificateNumber() == null || order.getCertificateNumber().isEmpty()) {
            throw new ValidationException("回收证明编号不能为空");
        }

        o = orderRepository.findFirstBySerialNumberAndDeletedIsFalse(order.getSerialNumber());
        if (order.getId() == null && o != null) {
            throw new ValidationException("已存在相同流水号");
        }

        if (order.getId() != null && o != null && !o.getId().equals(order.getId())) {
            throw new ValidationException("已存在相同流水号");
        }

        o = orderRepository.findFirstByCertificateNumberAndDeletedIsFalse(order.getCertificateNumber());
        if (order.getId() == null && o != null) {
            throw new ValidationException("已存在相同回收证明编号");
        }

        if (order.getId() != null && o != null && !o.getId().equals(order.getId())) {
            throw new ValidationException("已存在相同回收证明编号");
        }

        if (order.getCompanyName() == null || order.getCompanyName().isEmpty()) {
            throw new ValidationException("回收企业名称不能为空");
        }

        if (!superUser && !order.getCompanyName().equals(companyName)) {
            throw new ForbiddenException();
        }

        if (!generateCheckCode) {
            return order;
        }

        if (order.getOwnerName() == null || order.getOwnerName().isEmpty()) {
            throw new ValidationException("车主名称不能为空");
        }

        if (order.getIdNumber() == null || order.getIdNumber().isEmpty()) {
            throw new ValidationException("车主身份证号/代码证号不能为空");
        }

        if (order.getPhoneNumber() == null || order.getPhoneNumber().isEmpty()) {
            throw new ValidationException("车主联系电话不能为空");
        }

        if (order.getAddress() == null || order.getAddress().isEmpty()) {
            throw new ValidationException("车主地址不能为空");
        }

        if (order.getDeliveryDate() == null) {
            throw new ValidationException("交车日期不能为空");
        }

        if (order.getRegistrationDate() == null) {
            throw new ValidationException("注册登记日期不能为空");
        }

        if (order.getVehicleNumber() == null || order.getVehicleNumber().isEmpty()) {
            throw new ValidationException("车辆牌照号码不能为空");
        }

        if (order.getVehicleType() == null || order.getVehicleType().isEmpty()) {
            throw new ValidationException("车辆类型不能为空");
        }

        if (order.getVehicleNature() == null || order.getVehicleNature().isEmpty()) {
            throw new ValidationException("车辆使用性质不能为空");
        }

        if (order.getVehicleBrand() == null || order.getVehicleBrand().isEmpty()) {
            throw new ValidationException("车辆品牌不能为空");
        }

        if (order.getVehicleModel() == null || order.getVehicleModel().isEmpty()) {
            throw new ValidationException("车辆型号不能为空");
        }

        if (order.getVin() == null || order.getVin().isEmpty()) {
            throw new ValidationException("车辆识别代号不能为空");
        }

        if (order.getPowerType() == null || order.getPowerType().isEmpty()) {
            throw new ValidationException("动力类别不能为空");
        }

        if (order.getEngineNumber() == null || order.getEngineNumber().isEmpty()) {
            throw new ValidationException("发动机号码不能为空");
        }

        if (order.getProcessingDate() == null) {
            throw new ValidationException("办理日期不能为空");
        }

        if (order.getCancellationDate() == null) {
            throw new ValidationException("注销日期不能为空");
        }

        if (order.getVehicleManagementOffice() == null || order.getVehicleManagementOffice().isEmpty()) {
            throw new ValidationException("车管所不能为空");
        }

        if (order.getDeleted() != null && order.getDeleted() && generateCheckCode) {
            throw new ValidationException("订单不能同时生效及作废");
        }

        return order;
    }

    private Map<String, List<String>> getDataItems() {
        Map<String, List<String>> dataItems = new HashMap<>();
        dataItems.put("remark", new ArrayList<>());
        dataItems.put("vehicleType", new ArrayList<>());
        dataItems.put("vehicleNature", new ArrayList<>());
        dataItems.put("powerType", new ArrayList<>());
        Config config = configRepository.findFirstByOrderByIdAsc();
        if (config != null) {
            if (config.getDefaultRemark() != null && !config.getDefaultRemark().isEmpty()) {
                dataItems.get("remark").add(config.getDefaultRemark());
            }
            dataItems.put("vehicleType", splitText(config.getVehicleTypes()));
            dataItems.put("vehicleNature", splitText(config.getVehicleNatures()));
            dataItems.put("powerType", splitText(config.getPowerTypes()));
        }

        dataItems.put("companyName", orderRepository.findCompanyNames());
        dataItems.put("vehicleBrand", orderRepository.findVehicleBrands());
        dataItems.put("vehicleModel", orderRepository.findVehicleModels());
        dataItems.put("vehicleManagementOffice", orderRepository.findVehicleManagementOffices());
        dataItems.put("recoveredMaterials", orderRepository.findRecoveredMaterials());
        dataItems.put("unrevokedMaterials", orderRepository.findUnrevokedMaterials());
        return dataItems;
    }

    private List<String> splitText(String text) {
        List<String> list = new ArrayList<>();
        if (text != null && !text.isEmpty()) {
            list = Arrays.asList(text.split(","));
        }
        return list;
    }

}
